/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: thread.cpp
 *
 * Purpose: thread API implementation
 *
 * Developer:
 *   wen.gu , 2023-06-20
 *
 * TODO:
 *
 ***************************************************************************/
#include "config.h"

#if defined(HAS_POSIX_THREAD) && (HAS_POSIX_THREAD)
#include "xbus/core/xthread.h"

#include <pthread.h>
#include <errno.h>
#include <sched.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#include <mutex>


namespace xbus::core {

// Min/max thread priority range for QNX
static constexpr int32_t THD_PRIORITY_MIN = 8;
static constexpr int32_t THD_PRIORITY_MAX = 22;
static constexpr int32_t THD_PRIORITY_DEF = 10;

class XThread::Impl {
public:
    pthread_t thid_;
    pthread_t invliad_thid_;
    std::mutex lock_;
public:
    Impl() {
        invliad_thid_ = pthread_self();
        thid_ = invliad_thid_; 
    }

    bool apply_priority(int32_t level) {
        int32_t schedPolicy;
        struct sched_param schedParam;
        if (pthread_getschedparam(thid_, &schedPolicy, &schedParam) != 0) {
            return false;
        }

        schedParam.sched_priority = level;
        return pthread_setschedparam(thid_, schedPolicy, &schedParam) == 0;
    }

public:
    static void* thread_func(void *opaque) {
        XThread* thd = static_cast<XThread*>(opaque);
        //todo process, XThread might be started before mXThread is updated at start()
        //In this case is_self() might return with false even in self

        thd->run();
        return nullptr;
    }
};



XThread::XThread(std::string_view name, Runnable&& runnable)
    :priority_(THD_PRIORITY_DEF),
    impl_(new Impl),
    name_(name),
    runnable_(std::move(runnable)) {
    /** todo something */ 
    int32_t schedPolicy;
    struct sched_param schedParam;
    if (!pthread_getschedparam(pthread_self(), &schedPolicy, &schedParam)) {
        priority_ = schedParam.sched_priority;
    } 
}

XThread::~XThread() {
    /** todo something */ 
    //jion() ??
}


bool XThread::start() {
    std::lock_guard<std::mutex> _l(impl_->lock_);
    if (is_started()) {
        return true;
    }

    pthread_attr_t threadAttr;
    if (pthread_attr_init(&threadAttr) != 0) { 
        return false;
    }

    if (pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_JOINABLE) != 0) {
        // We no longer need the attribute structure
        (void)pthread_attr_destroy(&threadAttr);        
        return false;
    }

    if (pthread_create(&impl_->thid_, &threadAttr, Impl::thread_func, this) != 0) {
        // We no longer need the attribute structure
        (void)pthread_attr_destroy(&threadAttr);        
        return false;        
    }

    impl_->apply_priority(priority_);
    set_name(name_);

    // We no longer need the attribute structure
    (void)pthread_attr_destroy(&threadAttr);
    return true;
}

bool XThread::join() {
    if (is_self()) {
        return false;
    }
    return !pthread_join(impl_->thid_, 0);
}

bool XThread::is_started() {
    return !pthread_equal(impl_->invliad_thid_, impl_->thid_);
}

bool XThread::set_priority(int32_t level) {
    if ((level < THD_PRIORITY_MIN) || (level > THD_PRIORITY_MAX)) {
        return false;
    }

    priority_ = level;
    impl_->apply_priority(level);
    return true;
}

bool XThread::is_self() const {
    return !!pthread_equal(impl_->thid_, pthread_self());
}

bool XThread::set_name(const std::string& name) {
#if defined(HAS_POSIX_THREAD_SET_NAME_API) && (HAS_POSIX_THREAD_SET_NAME_API)
    return pthread_setname_np(impl_->thid_, name.c_str()) == 0;
#else
    printf("set thread name: %s, will not take effect\n", name.c_str());
    return false;
#endif
}

} //namespace xbus::core

#endif /** defined HAS_POSIX_THREAD */
